package rabbit.flt.r2dbc.test;

import com.fasterxml.jackson.databind.ObjectMapper;
import junit.framework.TestCase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rabbit.flt.common.trace.TraceData;
import rabbit.flt.test.common.spi.TestTraceHandler;
import reactor.netty.http.client.HttpClient;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;

import static rabbit.flt.common.trace.MessageType.R2DBC;

public class R2dbcCase {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    public void addUserCase() throws Exception {
        Semaphore semaphore = new Semaphore(0);
        Map<String, TraceData> map = new ConcurrentHashMap<>();
        TestTraceHandler.setDiscardDataHandler(d -> {
            logger.info("r2dbc: {}#{}#{}#{}", d.getNodeName(), d.getSpanId(), d.getNodeDesc(),
                    d.getApplicationCode());
            map.put(d.getSpanId(), d);
            semaphore.release();
        });
        String username = "lucy";
        User user = new ObjectMapper().readValue(call("/r2dbc/insert/" + username), User.class);
        TestCase.assertEquals(username, user.getUsername());
        semaphore.acquire(4);
        TestCase.assertTrue(map.get("0").getCost() >= map.get("0-0").getCost());
        TestCase.assertTrue(map.get("0-0").getCost() >= map.get("0-0-0").getCost());
        TestCase.assertTrue(map.get("0-0-0").getCost() >= map.get("0-0-0-0").getCost());
        TestCase.assertEquals(R2DBC.name(), map.get("0-0-0-0").getMessageType());
        TestCase.assertEquals("INSERT", map.get("0-0-0-0").getNodeName());
        TestCase.assertTrue(map.get("0-0-0-0").getCost() >= 0);
        TestTraceHandler.setDiscardDataHandler(null);
    }


    public void selectUserCase() throws Exception {
        Semaphore semaphore = new Semaphore(0);
        Map<String, TraceData> map = new ConcurrentHashMap<>();
        TestTraceHandler.setDiscardDataHandler(d -> {
            logger.info("r2dbc: {}#{}#{}#{}", d.getNodeName(), d.getSpanId(), d.getNodeDesc(),
                    d.getApplicationCode());
            map.put(d.getSpanId(), d);
            semaphore.release();
        });
        String username = "lucy";
        User user = new ObjectMapper().readValue(call("/r2dbc/select/" + username), User.class);
        TestCase.assertEquals(username, user.getUsername());
        semaphore.acquire(4);
        TestCase.assertTrue(map.get("0").getCost() >= map.get("0-0").getCost());
        TestCase.assertTrue(map.get("0-0").getCost() >= map.get("0-0-0").getCost());
        TestCase.assertTrue(map.get("0-0-0").getCost() >= map.get("0-0-0-0").getCost());
        TestCase.assertEquals(R2DBC.name(), map.get("0-0-0-0").getMessageType());
        TestCase.assertEquals("SELECT", map.get("0-0-0-0").getNodeName());
        TestCase.assertTrue(map.get("0-0-0-0").getCost() >= 0);
        TestTraceHandler.setDiscardDataHandler(null);
    }

    public void selectBySqlUserCase() throws Exception {
        Semaphore semaphore = new Semaphore(0);
        Map<String, TraceData> map = new ConcurrentHashMap<>();
        TestTraceHandler.setDiscardDataHandler(d -> {
            logger.info("r2dbc: {}#{}#{}#{}", d.getNodeName(), d.getSpanId(), d.getNodeDesc(),
                    d.getApplicationCode());
            map.put(d.getSpanId(), d);
            semaphore.release();
        });
        String username = "lucy";
        User user = new ObjectMapper().readValue(call("/r2dbc/select/bySql/" + username), User.class);
        TestCase.assertEquals(username, user.getUsername());
        semaphore.acquire(4);
        TestCase.assertTrue(map.get("0").getCost() >= map.get("0-0").getCost());
        TestCase.assertTrue(map.get("0-0").getCost() >= map.get("0-0-0").getCost());
        TestCase.assertTrue(map.get("0-0-0").getCost() >= map.get("0-0-0-0").getCost());
        TestCase.assertEquals(R2DBC.name(), map.get("0-0-0-0").getMessageType());
        TestCase.assertEquals("SELECT", map.get("0-0-0-0").getNodeName());
        TestCase.assertTrue(map.get("0-0-0-0").getCost() >= 0);
        TestTraceHandler.setDiscardDataHandler(null);
    }


    public void updateUserCase() throws Exception {
        Semaphore semaphore = new Semaphore(0);
        Map<String, TraceData> map = new ConcurrentHashMap<>();
        TestTraceHandler.setDiscardDataHandler(d -> {
            logger.info("r2dbc: {}#{}#{}#{}", d.getNodeName(), d.getSpanId(), d.getNodeDesc(),
                    d.getApplicationCode());
            map.put(d.getSpanId(), d);
            semaphore.release();
        });
        String username = "lucy";
        TestCase.assertEquals("1", call("/r2dbc/update/" + username + "/newUser"));
        semaphore.acquire(4);
        TestCase.assertTrue(map.get("0").getCost() >= map.get("0-0").getCost());
        TestCase.assertTrue(map.get("0-0").getCost() >= map.get("0-0-0").getCost());
        TestCase.assertTrue(map.get("0-0-0").getCost() >= map.get("0-0-0-0").getCost());
        TestCase.assertEquals(R2DBC.name(), map.get("0-0-0-0").getMessageType());
        TestCase.assertEquals("UPDATE", map.get("0-0-0-0").getNodeName());
        TestCase.assertTrue(map.get("0-0-0-0").getCost() >= 0);
        TestTraceHandler.setDiscardDataHandler(null);
    }

    public void deleteUserCase() throws Exception {
        Semaphore semaphore = new Semaphore(0);
        Map<String, TraceData> map = new ConcurrentHashMap<>();
        TestTraceHandler.setDiscardDataHandler(d -> {
            logger.info("r2dbc: {}#{}#{}#{}", d.getNodeName(), d.getSpanId(), d.getNodeDesc(),
                    d.getApplicationCode());
            map.put(d.getSpanId(), d);
            semaphore.release();
        });
        TestCase.assertEquals("1", call("/r2dbc/delete/newUser"));
        semaphore.acquire(4);
        TestCase.assertTrue(map.get("0").getCost() >= map.get("0-0").getCost());
        TestCase.assertTrue(map.get("0-0").getCost() >= map.get("0-0-0").getCost());
        TestCase.assertTrue(map.get("0-0-0").getCost() >= map.get("0-0-0-0").getCost());
        TestCase.assertEquals(R2DBC.name(), map.get("0-0-0-0").getMessageType());
        TestCase.assertEquals("DELETE", map.get("0-0-0-0").getNodeName());
        TestCase.assertTrue(map.get("0-0-0-0").getCost() >= 0);
        TestTraceHandler.setDiscardDataHandler(null);
    }

    public void multiAddCase() throws Exception {
        Semaphore semaphore = new Semaphore(0);
        Map<String, TraceData> map = new ConcurrentHashMap<>();
        TestTraceHandler.setDiscardDataHandler(d -> {
            logger.info("multi add: {}#{}#{}#{}", d.getNodeName(), d.getSpanId(), d.getNodeDesc(),
                    d.getApplicationCode());
            map.put(d.getSpanId(), d);
            semaphore.release();
        });
        String username = "multi";
        TestCase.assertEquals("5", call("/r2dbc/multiAdd/" + username));
        semaphore.acquire(9);
        TestCase.assertTrue(map.get("0").getCost() >= map.get("0-0").getCost());
        TestCase.assertTrue(map.get("0-0").getCost() >= map.get("0-0-0").getCost());
        TestCase.assertTrue(map.get("0-0-0").getCost() >= map.get("0-0-0-0").getCost());
        TestCase.assertEquals(R2DBC.name(), map.get("0-0-0-0").getMessageType());
        TestCase.assertEquals("INSERT", map.get("0-0-0-0").getNodeName());
        TestCase.assertEquals("INSERT", map.get("0-0-0-1").getNodeName());
        TestCase.assertEquals("INSERT", map.get("0-0-0-2").getNodeName());
        TestCase.assertEquals("INSERT", map.get("0-0-0-3").getNodeName());
        TestCase.assertEquals("INSERT", map.get("0-0-0-4").getNodeName());
        TestCase.assertEquals("COUNT", map.get("0-0-0-5").getNodeName());
        logger.info("{} cost: {}", map.get("0-0-0").getNodeName(), map.get("0-0-0").getCost());
        logger.info("{} cost: {}", map.get("0-0-0-0").getNodeName(), map.get("0-0-0-0").getCost());
        logger.info("{} cost: {}", map.get("0-0-0-1").getNodeName(), map.get("0-0-0-1").getCost());
        logger.info("{} cost: {}", map.get("0-0-0-2").getNodeName(), map.get("0-0-0-2").getCost());
        logger.info("{} cost: {}", map.get("0-0-0-3").getNodeName(), map.get("0-0-0-3").getCost());
        logger.info("{} cost: {}", map.get("0-0-0-4").getNodeName(), map.get("0-0-0-4").getCost());
        logger.info("{} cost: {}", map.get("0-0-0-5").getNodeName(), map.get("0-0-0-5").getCost());
        TestCase.assertTrue(map.get("0-0-0").getCost() >= map.get("0-0-0-0").getCost() +
                map.get("0-0-0-1").getCost() +
                map.get("0-0-0-2").getCost() +
                map.get("0-0-0-3").getCost() +
                map.get("0-0-0-4").getCost() +
                map.get("0-0-0-5").getCost());
        TestTraceHandler.setDiscardDataHandler(null);
    }


    public void errorSqlCase() throws Exception {
        Semaphore semaphore = new Semaphore(0);
        Map<String, TraceData> map = new ConcurrentHashMap<>();
        TestTraceHandler.setDiscardDataHandler(d -> {
            logger.info("error sql: {}#{}#{}#{}", d.getNodeName(), d.getSpanId(), d.getNodeDesc(),
                    d.getApplicationCode());
            map.put(d.getSpanId(), d);
            semaphore.release();
        });
        call("/r2dbc/errorSql");
        semaphore.acquire(4);
        TestCase.assertTrue(map.get("0").getCost() >= map.get("0-0").getCost());
        TestCase.assertTrue(map.get("0-0").getCost() >= map.get("0-0-0").getCost());
        TestCase.assertTrue(map.get("0-0-0").getCost() >= map.get("0-0-0-0").getCost());
        TestCase.assertEquals(R2DBC.name(), map.get("0-0-0-0").getMessageType());
        TestCase.assertEquals(TraceData.Status.ERR, map.get("0-0-0-0").getStatus());
        TestCase.assertTrue(map.get("0-0-0-0").getCost() >= 0);
        TestTraceHandler.setDiscardDataHandler(null);
    }

    private String call(String path) {
        String uri = "http://localhost:8888" + path;
        return HttpClient.create().post().uri(uri)
                .responseSingle((httpClientResponse, byteBufMono) -> byteBufMono.asByteArray().map(String::new))
                .block();
    }
}
